#include "stdafx.h"
#include "MouseTracerManagerImpl2.h"
#include "MouseTracer.h"
#include "MGCfg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

HHOOK CMouseTracerManagerImpl2::m_hHook = NULL;
CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl2> > CMouseTracerManagerImpl2::m_mouseTracer;

BOOL CMouseTracerManagerImpl2::m_bValidTarget = FALSE;


HWND CMouseTracerManagerImpl2::m_hWnd = NULL;

const TCHAR g_szClassName[] = _T("MouseCaptureWnd");


CMouseTracerManagerImpl2::CMouseTracerManagerImpl2()
{
	// Create a temporary window whose sole and only purpose is receiving WM_CAPTURECHANGED message
	// since ::SetWindowsHookEx(WH_MOUSE) doesn't seem to forward this message to MouseProc callback
	WNDCLASSEX wc;
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.lpszClassName = g_szClassName;
	wc.hInstance = ::GetModuleHandle(NULL);
	wc.lpfnWndProc = ::DefWindowProc;
	wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = NULL;
	wc.lpszMenuName = NULL;
	wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE);
	wc.style = 0;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hIconSm = 0;
	
	if (!m_hWnd)
	{
#ifdef _DEBUG
		if (!::RegisterClassEx(&wc))
		{
			TRACE("\nWarning: %s (%d) CMouseTracerManagerImpl2::CMouseTracerManagerImpl2() - Failed to register window class.\n\n", __FILE__, __LINE__);
		}
#else
		::RegisterClassEx(&wc);
#endif
	}
	
	m_hWnd = ::CreateWindowEx(0, g_szClassName, _T("MouseTracerWindow"), WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, ::GetModuleHandle(NULL), NULL);
#ifdef _DEBUG
	if (!m_hWnd || !::IsWindow(m_hWnd))
	{
		TRACE("\nWarning: %s (%d) CMouseTracerManagerImpl2::CMouseTracerManagerImpl2() - Failed to create a registered window.\n\n", __FILE__, __LINE__);
	}
#endif
}


CMouseTracerManagerImpl2::~CMouseTracerManagerImpl2()
{
	if (m_hHook)
	{
		::UnhookWindowsHookEx(m_hHook);
		m_hHook = NULL;
	}
	
	if (m_hWnd)
	{
		::DestroyWindow(m_hWnd);
		m_hWnd = NULL;
	}
}

/**
*	CMouseTracerManagerImpl2::Setup(HINSTANCE hMod)
*
*
*	@return BOOL
*
*/

BOOL CMouseTracerManagerImpl2::Setup(HINSTANCE hMod)
{
	if (!m_hWnd)
		return FALSE;
	
	if (m_hHook)
		return TRUE;
	
	m_mouseTracer.Hook(m_hWnd);
	//  Using app's main window to receive WM_CAPTURECHANGED message instead of the temporary window didn't work! :P
	//	m_mouseTracer.Hook(AfxGetMainWnd()->GetSafeHwnd());
	
	if(hMod == NULL)
		hMod = (HINSTANCE)::GetModuleHandle(NULL);
	
	m_hHook = ::SetWindowsHookEx(WH_MOUSE, MouseProc, hMod, ::GetCurrentThreadId());
	
	return !(m_hHook == NULL);
}

/**
*	CMouseTracerManagerImpl2::Remove()
*
*
*	@return void
*
*/

void CMouseTracerManagerImpl2::Remove()
{
	if (m_hHook)
	{
		::UnhookWindowsHookEx(m_hHook);
		m_hHook = NULL;
	}
	
	if (m_mouseTracer.IsHooked())
	{
		m_mouseTracer.Unhook();
	}
}

/**
*	CMouseTracerManagerImpl2::MouseProc()
*
*	@param int nCode
*	@param WPARAM wParam
*	@param LPARAM lParam
*
*	@return LRESULT CALLBACK
*
*/

LRESULT CALLBACK CMouseTracerManagerImpl2::MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if(nCode < 0)
		return ::CallNextHookEx(m_hHook, nCode, wParam, lParam);
	
    if (nCode == HC_ACTION)
    {
        if (lParam)
        {
#if(_WIN32_WINNT >= 0x0500)
            MOUSEHOOKSTRUCTEX *pMHEX = reinterpret_cast<MOUSEHOOKSTRUCTEX *>(lParam);
			MOUSEHOOKSTRUCT *pMH = (MOUSEHOOKSTRUCT *)pMHEX;
#else	// #if(_WIN32_WINNT >= 0x0500)
			MOUSEHOOKSTRUCT *pMH = reinterpret_cast<MOUSEHOOKSTRUCT *>(lParam);
#endif	// #if(_WIN32_WINNT >= 0x0500)
			
			LRESULT lResult;
			BOOL bResult = FALSE;
			switch (wParam)
			{
			case WM_RBUTTONDOWN:
			case WM_NCRBUTTONDOWN:
				if (MGCfg::Get().Option().GetMGAButton() == MG::BUTTON_RIGHT)
				{
					if (!m_mouseTracer.IsTemporarySuspended())
					{
						// added option to identify target window class name
						if (MGCfg::Get().Option().IsShowRecognitionOnStatusBar() && MGCfg::Get().Option().IsIdentifyTargetWindowClass())
							MGCfg::Get().Action().IdentifyTargetWindowClass(pMH->hwnd);
						
						if (MGCfg::Get().Action().CheckTargetValid(pMH->hwnd) != -1 && !AfxGetMainWnd()->GetTopLevelFrame()->InModalState())
						{
							CMouseTracerManagerImpl2::m_bValidTarget = TRUE;
							
							// activate the windows and bring it up to the top position in z-order
							::SendMessage(pMH->hwnd, WM_ACTIVATE, (WPARAM)WA_CLICKACTIVE, NULL);
							::SendMessage(AfxGetMainWnd()->GetTopLevelFrame()->GetSafeHwnd(), WM_MDIACTIVATE, (WPARAM)pMH->hwnd, NULL);
							
							CWnd *pWnd = CWnd::FromHandle(pMH->hwnd);
							if (pWnd)
							{
								CFrameWnd *pFrame = (CFrameWnd *)pWnd;
								if (!pWnd->GetRuntimeClass()->IsDerivedFrom(RUNTIME_CLASS(CFrameWnd)))
									pFrame = pWnd->GetParentFrame();
								
								if (pFrame)
									::SendMessage(pFrame->GetParent()->GetSafeHwnd(), WM_MDIACTIVATE, (WPARAM)pFrame->GetSafeHwnd(), NULL);
							}
						}
						else 
						{
							CMouseTracerManagerImpl2::m_bValidTarget = FALSE;
						}
					}
				}
				
				if (CMouseTracerManagerImpl2::m_bValidTarget)
				{
					bResult = m_mouseTracer.ProcessWindowMessage((UINT)wParam, 0, MAKELONG((WORD)(pMH->pt.x), (WORD)(pMH->pt.y)), lResult);
					if (bResult)
						return (LRESULT) - 1;
				}
				break;
				
			case WM_RBUTTONUP:
			case WM_NCRBUTTONUP:
				if (CMouseTracerManagerImpl2::m_bValidTarget)
				{
					if (MGCfg::Get().Option().GetMGAButton() == MG::BUTTON_RIGHT)
						CMouseTracerManagerImpl2::m_bValidTarget = FALSE;
					
					bResult = m_mouseTracer.ProcessWindowMessage((UINT)wParam, 0, MAKELONG((WORD)(pMH->pt.x), (WORD)(pMH->pt.y)), lResult);
					if (bResult)
						return (LRESULT) - 1;
				}
				break;
				
			case WM_MBUTTONDOWN:
			case WM_NCMBUTTONDOWN:
				if (MGCfg::Get().Option().GetMGAButton() == MG::BUTTON_MIDDLE)
				{
					if (!m_mouseTracer.IsTemporarySuspended())
					{
						// added option to identify target window class name
						if (MGCfg::Get().Option().IsShowRecognitionOnStatusBar() && MGCfg::Get().Option().IsIdentifyTargetWindowClass())
							MGCfg::Get().Action().IdentifyTargetWindowClass(pMH->hwnd);
						
						if (MGCfg::Get().Action().CheckTargetValid(pMH->hwnd) != -1 && !AfxGetMainWnd()->GetTopLevelFrame()->InModalState())
						{
							CMouseTracerManagerImpl2::m_bValidTarget = TRUE;
							
							// activate the windows and bring it up to the top position in z-order
							::SendMessage(pMH->hwnd, WM_ACTIVATE, (WPARAM)WA_CLICKACTIVE, NULL);
							::SendMessage(AfxGetMainWnd()->GetTopLevelFrame()->GetSafeHwnd(), WM_MDIACTIVATE, (WPARAM)pMH->hwnd, NULL);
							
							CWnd *pWnd = CWnd::FromHandle(pMH->hwnd);
							if (pWnd)
							{
								CFrameWnd *pFrame = (CFrameWnd *)pWnd;
								if (!pWnd->GetRuntimeClass()->IsDerivedFrom(RUNTIME_CLASS(CFrameWnd)))
									pFrame = pWnd->GetParentFrame();
								
								if (pFrame)
									::SendMessage(pFrame->GetParent()->GetSafeHwnd(), WM_MDIACTIVATE, (WPARAM)pFrame->GetSafeHwnd(), NULL);
							}
						}
						else 
						{
							CMouseTracerManagerImpl2::m_bValidTarget = FALSE;
						}
					}
				}
				
				if (CMouseTracerManagerImpl2::m_bValidTarget)
				{
					bResult = m_mouseTracer.ProcessWindowMessage((UINT)wParam, 0, MAKELONG((WORD)(pMH->pt.x), (WORD)(pMH->pt.y)), lResult);
					if (bResult)
						return (LRESULT) - 1;
				}
				break;
				
			case WM_MBUTTONUP:
			case WM_NCMBUTTONUP:
				if (CMouseTracerManagerImpl2::m_bValidTarget)
				{
					if (MGCfg::Get().Option().GetMGAButton() == MG::BUTTON_MIDDLE)
						CMouseTracerManagerImpl2::m_bValidTarget = FALSE;
					
					bResult = m_mouseTracer.ProcessWindowMessage((UINT)wParam, 0, MAKELONG((WORD)(pMH->pt.x), (WORD)(pMH->pt.y)), lResult);
					if (bResult)
						return (LRESULT) - 1;
				}
				break;
				
			case WM_LBUTTONDOWN:
			case WM_NCLBUTTONDOWN:
				
			case WM_LBUTTONUP:
			case WM_NCLBUTTONUP:
				
			case WM_MOUSEWHEEL:
				
			case WM_MOUSEMOVE:
			case WM_NCMOUSEMOVE:
				if (CMouseTracerManagerImpl2::m_bValidTarget)
				{
#if(_WIN32_WINNT >= 0x0500)
					bResult = m_mouseTracer.ProcessWindowMessage((UINT)wParam, pMHEX->mouseData, MAKELONG((WORD)(pMH->pt.x), (WORD)(pMH->pt.y)), lResult);
#else	// #if(_WIN32_WINNT >= 0x0500)
					bResult = m_mouseTracer.ProcessWindowMessage((UINT)wParam, 0, MAKELONG((WORD)(pMH->pt.x), (WORD)(pMH->pt.y)), lResult);
#endif	// #if(_WIN32_WINNT >= 0x0500)
					
					if (bResult)
						return (LRESULT) - 1;
				}
				break;
				
			case WM_RBUTTONDBLCLK:
			case WM_LBUTTONDBLCLK:
			case WM_MBUTTONDBLCLK:
				if (MGCfg::Get().Action().CheckTargetValid(pMH->hwnd) != -1 && !AfxGetMainWnd()->GetTopLevelFrame()->InModalState())
				{
					//	CMouseTracerManagerImpl2::m_bValidTarget = TRUE;
					
					bResult = m_mouseTracer.ProcessWindowMessage((UINT)wParam, 0, MAKELONG((WORD)(pMH->pt.x), (WORD)(pMH->pt.y)), lResult);
					if (bResult)
						return (LRESULT) - 1;
				}
				break;
				
			case WM_NCLBUTTONDBLCLK:
			case WM_NCMBUTTONDBLCLK:
			case WM_NCRBUTTONDBLCLK:
				break;
				
			default:
				ASSERT(FALSE);
				break;
			}
		}
    }
	
	return ::CallNextHookEx(m_hHook, nCode, wParam, lParam);
}
